<template>
  <!-- 数值区间控件 -->
  <div v-if="!invalid" class="comp-range-number">
    <!-- realStep {{realStep}}<br />
   sliderStep {{sliderStep}}<br />
   precision {{precision}}<br />
   min {{min}}<br/>
   max {{max}}<br/>
   value {{value}}<br />
   calcMin() {{calcMin()}}<br />
   calcMax() {{calcMax()}}<br /> -->
    <a-slider
      :disabled="!inited || disabled || min === max"
      range
      size="small"
      :value="value"
      :included="true"
      :min="calcMin() || min"
      :step="precision ? 0.1 ** precision : sliderStep"
      :tooltip-visible="false"
      :max="calcMax() || max"
      @change="
        (value) => {
          onFieldChange(fixValue(value))
        }
      "
    />
    <div class="row">
      <a-input-number
        class="number-box"
        :disabled="!inited || disabled"
        :min="calcMin() || min"
        :max="value[1] || calcMax() || max"
        :value="value[0]"
        :step="realStep"
        :precision="precision"
        :included="true"
        @change="
          (value_) => {
            if (/\.$/.test(value_)) {
              return
            }
            onFieldChange([value_, value[1]])
          }
        "
      ></a-input-number>
      <span class="line">—</span>
      <a-input-number
        class="number-box"
        :included="true"
        :disabled="!inited || disabled"
        :value="value[1]"
        :step="realStep"
        :precision="precision"
        :min="value[0] || calcMin() || min"
        :max="max"
        @change="
          (value_) => {
            if (/\.$/.test(value_)) {
              return
            }
            onFieldChange([value[0], value_])
          }
        "
      ></a-input-number>
    </div>
  </div>
  <div v-else class="comp-range-number">
    <a-slider disabled></a-slider>
    <div class="row">
      <a-input-number class="number-box" disabled> </a-input-number>
      <span class="line">—</span>
      <a-input-number class="number-box" disabled> </a-input-number>
    </div>
  </div>
</template>
<script lang="ts">
import { queryColumnInfo } from '@/api/widget'
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import * as Big from 'big.js'
import { isNumber } from 'lodash'

export interface IRangeNumberConfig {
  tableName: string
  colName: string
  type: number
  dataId: number
  [key: string]: any
}

/** 筛选表单 */
@Component({})
export default class RangeNumber extends Vue {
  /** 区间配置 */
  @Prop() config!: IRangeNumberConfig

  /** 失效 */
  @Prop() invalid!: boolean

  /** 父组件实例 */
  @Prop() parent!: any

  /** 禁用 */
  @Prop() disabled!: boolean

  /** 绑定值 */
  @Prop({
    default() {
      return [-Infinity, Infinity]
    },
  })
  value!: [number, number]

  /** 默认值 */
  @Prop() defaultValue!: [number, number]

  /** 步长 */
  @Prop() step!: number

  /** 最小值 */
  public max: number | null = null

  /* 最大值 */

  public min: number | null = null

  /** 包含误差的最小值 */
  calcMin() {
    if (this.min !== null && this.max !== null && this.realStep) {
      return this.min - 0.1 ** this.precision
    }
    return null
  }

  /** 包含误差最大值 */
  calcMax() {
    if (this.min !== null && this.max !== null && this.realStep) {
      return this.max + 0.1 ** this.precision
    }
    return null
  }

  /** 这里为了精度采用了Big.js提高精度 */
  get realStep() {
    if (this.invalid) {
      return 1
    }
    const condition = isNumber(this.max) && isNumber(this.min)
    return condition
      ? Number.parseFloat(
          new (Big as any)(this.max as number)
            .minus(this.min as number)
            .times(this.step)
            .div(100)
            .valueOf()
        )
      : 1
  }

  /** slider 不遵寻步进，因为有除不尽的情况 */
  get sliderStep() {
    if (this.invalid) {
      return 1
    }
    return this.max === null || this.min === null || !this.inited
      ? 1
      : Number.parseFloat(
          new (Big as any)(this.max as number)
            .minus(this.min as number)
            .div(100)
            .valueOf()
        )
  }

  precision: number = 2

  @Watch('sliderStep', { immediate: true })
  getSliderStep(newValue: number) {
    let pResult: number = 2
    if (newValue < 1 && newValue > 0) {
      let temporary = 10
      while (temporary * newValue < 1) {
        pResult += 1
        temporary *= 10
      }
    }
    this.precision = pResult
  }

  /** 是否初始化 */
  inited: boolean = false

  public onFieldChange(value: [number, number]) {
    const valueEmit = this.optimization(value)
    this.$emit('input', valueEmit)
    this.$emit('change', valueEmit)
  }

  /** 优化筛选值, 如果边界数值小数小于精度，则用边界值填充 */
  public optimization(value: [number, number]) {
    if (!isNumber(value[0]) || !isNumber(value[1])) {
      return value
    }
    return [
      value[0].toString().split('.').length < this.precision &&
      this.min !== null &&
      value[0] < this.min
        ? this.min
        : value[0],
      value[1].toString().split('.').length < this.precision &&
      this.max !== null &&
      value[1] > this.max
        ? this.max
        : value[1],
    ]
  }

  created() {
    this.init()
  }

  @Watch('config', { immediate: false, deep: true })
  getConfig(newValue: IRangeNumberConfig, oldValue: IRangeNumberConfig) {
    if (newValue?.colName !== oldValue?.colName) {
      this.inited = false
      this.$nextTick(() => {
        this.init()
      })
    }
    return newValue
  }

  getCacheData(key: string) {
    if (this.parent && this.parent.getColumnInfoByCache) {
      return this.parent.getColumnInfoByCache(key)
    }
    return null
  }

  /** 获取缓存字段 */
  getCacheKey() {
    return `${this.config.tableName}_${this.config.colName}_${this.config.type}_${this.config.dataId}`
  }

  /** 设置最大值，最小值关联逻辑 */
  private callbackMaxMin(min: any, max: any, cacheKey?: string) {
    this.min = min
    this.max = max
    const minSet = this.min !== null ? this.min : this.value[0]
    const maxSet = this.max !== null ? this.max : this.value[1]
    this.onFieldChange([minSet, maxSet])
    if (cacheKey && this.parent && this.parent.setColumnInfo) {
      this.parent.setColumnInfo(cacheKey, { min: this.min, max: this.max })
    }
  }

  fixValue(value: [number, number]) {
    return [
      Number.parseFloat(value[0].toFixed(this.precision)),
      Number.parseFloat(value[1].toFixed(this.precision)),
    ]
  }

  init() {
    //  如果不可用直接
    if (this.invalid) {
      this.min = null
      this.max = null
      this.inited = true
      return
    }
    /** graph数据前端已经计算好最大值与最小值 */
    if (this.config.isGraphColumn) {
      this.callbackMaxMin(this.config.min, this.config.max)
      this.inited = true
      return
    }

    const cacheKey = this.getCacheKey()
    const cacheData = this.getCacheData(cacheKey)
    if (cacheData) {
      this.min = cacheData.min
      this.max = cacheData.max
      this.inited = true
      return
    }

    queryColumnInfo({
      data: {
        ...this.config,
        pageSize: 1,
        curPage: 1,
      },
    }).then((response) => {
      if (response.data.code === 100) {
        this.callbackMaxMin(
          response.data?.result?.min,
          response.data?.result?.max,
          cacheKey
        )
        this.inited = true
      }
    })
  }
}
</script>
<style lang="less" scoped>
.comp-range-number {
  height: auto;
  width: 100%;

  .selector-item {
    height: 24px;
    width: 100%;

    /deep/ .ant-select-selection-selected-value {
      font-size: 14px;
      line-height: 20px;
    }

    /deep/ .ant-select-selection {
      height: 24px;
    }

    /deep/ .ant-select-selection__rendered {
      height: 25px;
      line-height: 16px;
    }

    /deep/ .ant-select-selection__choice {
      color: #373b52;
      font-size: 12px;
      height: 22px;
      line-height: 20px;
    }
  }

  .row {
    align-items: center;
    display: flex;
    flex-flow: row nowrap;
  }

  .number-box {
    height: 24px;
    width: calc(50% - 20px);

    /deep/ .ant-input-number-input-wrap {
      height: 24px;
    }

    /deep/ .ant-input-number-input {
      height: 24px;
      line-height: 18px;
    }
  }

  .line {
    color: #5d637e;
    display: block;
    display: inline-block;
    height: 20%;
    padding: 0 10px;
    position: relative;
    text-align: center;
    width: 40px;

    // &::after {
    //   background: #5d637e;
    //   content: ' ';
    //   display: block;
    //   height: 1px;
    //   left: 50%;
    //   position: absolute;
    //   top: 50%;
    //   transform: translateX(-50%);
    //   width: 80%;
    // }
  }
}
</style>
